// Copyright (c) Lawrence Livermore National Security, LLC and other VisIt
// Project developers.  See the top-level LICENSE file for dates and other
// details.  No copyright assignment is required to contribute to VisIt.

// ************************************************************************* //
//  File: avtDisplaceFilter.C
// ************************************************************************* //

#include <avtDisplaceFilter.h>

#include <vtkCellData.h>
#include <vtkCellDataToPointData.h>
#include <vtkPointData.h>
#include <vtkPointSet.h>
#include <vtkRectilinearGrid.h>
#include <vtkStructuredGrid.h>
#include <vtkWarpVector.h>

#include <avtDatasetExaminer.h>
#include <avtExtents.h>
#include <vtkVisItUtility.h>

#include <DebugStream.h>
#include <ImproperUseException.h>
#include <InvalidDimensionsException.h>


// ****************************************************************************
//  Method: avtDisplaceFilter constructor
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Mon Nov 5 15:35:46 PST 2001
//
//  Modifications:
//
//    Hank Childs, Tue Jun 29 07:59:53 PDT 2004
//    Removed initialization of issuedWarning; data member has been removed.
//
//    Hank Childs, Fri May 18 16:01:06 PDT 2007
//    Initialize factor, variable.
//
//    Hank Childs, Thu Aug 26 13:47:30 PDT 2010
//    Change extents names.
//
// ****************************************************************************

avtDisplaceFilter::avtDisplaceFilter()
{
    OverrideOriginalSpatialExtents();
    factor   = 1.;
    variable = "default";
}


// ****************************************************************************
//  Method: avtDisplaceFilter destructor
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Mon Nov 5 15:35:46 PST 2001
//
//  Modifications:
//
// ****************************************************************************

avtDisplaceFilter::~avtDisplaceFilter()
{
}


// ****************************************************************************
//  Method: avtDisplaceFilter::SetVariable
//
//  Purpose:
//      Sets the variable to displace by.
//
//  Programmer: Hank Childs
//  Creation:   May 18, 2007
//
// ****************************************************************************

void
avtDisplaceFilter::SetVariable(const std::string &v)
{
    variable = v;
    if (strcmp(v.c_str(), "default") != 0)
    {
        SetActiveVariable(v.c_str());
    }
}


// ****************************************************************************
//  Method: avtDisplaceFilter::ExecuteData
//
//  Purpose:
//      Sends the specified input and output through the Displace filter.
//
//  Arguments:
//      in_dr      The input data representation.
//
//  Returns:       The output data representation.
//
//  Programmer: childs -- generated by xml2info
//  Creation:   Mon Nov 5 15:35:46 PST 2001
//
//  Modifications:
//    Kathleen Bonnell, Fri Feb  8 11:03:49 PST 2002
//    vtkVectors has been deprecated in VTK 4.0, use vtkDataArray instead.
//
//    Hank Childs, Wed Sep 11 08:34:03 PDT 2002
//    Fixed memory leak.
//
//    Hank Childs, Thu May  6 10:04:26 PDT 2004
//    Operate on rectilinear grids as well.
//
//    Hank Childs, Tue Jun 29 07:21:32 PDT 2004
//    Do not issue a warning if we get cell centered data.
//
//    Hank Childs, Tue Jun  7 09:35:31 PDT 2005
//    Improve handling of cell-centered vectors.
//
//    Hank Childs, Thu Oct  6 16:50:11 PDT 2005
//    Added support for displacing by a default variable ['6654]
//
//    Hank Childs, Fri Sep 28 07:40:16 PDT 2007
//    Fix bug with cell centered data and rectilinear grids.
//
//    Kathleen Biagas, Tue Aug 21 16:52:12 MST 2012
//    Preserve coordinate type.
//
//    Kathleen Biagas, Mon Jan 28 10:51:32 PST 2013
//    Call Update on filter, not data object.
//
//    Eric Brugger, Mon Jul 21 10:38:19 PDT 2014
//    Modified the class to work with avtDataRepresentation.
// 
//    Justin Privitera, Thu Nov  7 11:16:13 PST 2024
//    Added dimension check from preexecute to this function instead.
//
// ****************************************************************************

avtDataRepresentation *
avtDisplaceFilter::ExecuteData(avtDataRepresentation *in_dr)
{
    //
    // Get the VTK data set.
    //
    vtkDataSet *in_ds = in_dr->GetDataVTK();

    vtkDataSet *tmp_ds = NULL;

    const char *displace_var = variable.c_str();
    if (strcmp(displace_var, "default") == 0)
        displace_var = pipelineVariable;

    in_ds->GetPointData()->SetActiveVectors(displace_var);
    vtkDataArray *vecs = in_ds->GetPointData()->GetVectors();

    //
    // If we didn't get a vector variable, then see if there is a cell
    // centered vector variable and interpolate that to the nodes.
    //
    if (vecs == NULL)
    {
        vtkDataArray *cell_vecs = in_ds->GetCellData()->GetArray(displace_var);
        if (cell_vecs == NULL)
        {
            EXCEPTION0(ImproperUseException);
        }

        vtkDataSet *one_var_ds = (vtkDataSet *) in_ds->NewInstance();
        one_var_ds->CopyStructure(in_ds);
        one_var_ds->GetCellData()->AddArray(cell_vecs);

        vtkCellDataToPointData *cd2pd = vtkCellDataToPointData::New();
        cd2pd->SetInputData(one_var_ds);
        cd2pd->Update();
        vtkDataSet *pt_one_var_ds = cd2pd->GetOutput();

        tmp_ds = (vtkDataSet *) in_ds->NewInstance();
        tmp_ds->ShallowCopy(in_ds);
        vecs = pt_one_var_ds->GetPointData()->GetArray(displace_var);
        tmp_ds->GetPointData()->AddArray(vecs);
        tmp_ds->GetPointData()->SetActiveVectors(displace_var);

        in_ds = tmp_ds;
        cd2pd->Delete();
        one_var_ds->Delete();
    }

    const int num_comps = vecs->GetNumberOfComponents();
    if (num_comps != 3 && 
        !(num_comps == 2 &&
          GetInput()->GetInfo().GetAttributes().GetSpatialDimension() == 2))
    {
        EXCEPTION2(InvalidDimensionsException, "The displace operator",
                   "vector");
    }

    vtkDataSet *rv = NULL;
    if (in_ds->GetDataObjectType() == VTK_POLY_DATA 
        || in_ds->GetDataObjectType() == VTK_STRUCTURED_GRID 
        || in_ds->GetDataObjectType() == VTK_UNSTRUCTURED_GRID)
    {
        vtkWarpVector *warp = vtkWarpVector::New();
        warp->SetInputData((vtkPointSet *)in_ds);
        warp->SetScaleFactor(factor);
        warp->Update();
        rv = warp->GetOutput();
        rv->Register(NULL);

        warp->Delete();
    }
    else if (in_ds->GetDataObjectType() == VTK_RECTILINEAR_GRID)
    {
        vtkRectilinearGrid *rg = (vtkRectilinearGrid *) in_ds;
        vtkStructuredGrid *sg  = vtkStructuredGrid::New();
        int dims[3];
        rg->GetDimensions(dims);
        sg->SetDimensions(dims);
        sg->GetCellData()->ShallowCopy(rg->GetCellData());
        sg->GetPointData()->ShallowCopy(rg->GetPointData());
        vtkPoints *pts = vtkVisItUtility::NewPoints(in_ds);
        int npts = dims[0]*dims[1]*dims[2];
        pts->SetNumberOfPoints(npts);
        for (int i = 0 ; i < npts ; i++)
        {
            int xi = i % dims[0];
            int yi = (i / dims[0]) % dims[1];
            int zi = i / (dims[0]*dims[1]);
            double x = rg->GetXCoordinates()->GetTuple1(xi);
            double y = rg->GetYCoordinates()->GetTuple1(yi);
            double z = rg->GetZCoordinates()->GetTuple1(zi);
            double *vec = vecs->GetTuple3(i);
            x += factor*vec[0];
            y += factor*vec[1];
            z += factor*vec[2];
            pts->SetPoint(i, x, y, z);
        }
        sg->SetPoints(pts);
        pts->Delete();
 
        rv = sg;
    }
    else
    {
        debug1 << "Unable to determine dataset type for displace operator" 
               << endl;
        EXCEPTION0(ImproperUseException);
    }

    //
    // Clean up memory.
    //
    if (tmp_ds != NULL)
    {
        rv->GetPointData()->RemoveArray(displace_var);
        tmp_ds->Delete();
    }

    avtDataRepresentation *out_dr = new avtDataRepresentation(rv,
        in_dr->GetDomain(), in_dr->GetLabel());

    rv->Delete();

    return out_dr;
}


// ****************************************************************************
//  Method: avtDisplaceFilter::PreExecute
//
//  Purpose:
//      Check to see if the "default" variable is a valid variable.
//
//  Programmer: Hank Childs
//  Creation:   September 5, 2006
//
//  Modifications:
//
//    Hank Childs, Fri May 18 16:19:20 PDT 2007
//    Call avtDataTreeIterator's PostExecute, not avtPluginDataTreeIterator, since the 
//    inheritance changed.
//
//    Hank Childs, Fri Sep 28 07:14:14 PDT 2007
//    Allow for 2D vectors.
// 
//    Justin Privitera, Wed Nov  6 16:41:21 PST 2024
//    Remove the requirement variable dimension requirement since we cannot
//    know the dimension of expressions until we have used them.
//
// ****************************************************************************

void
avtDisplaceFilter::PreExecute(void)
{
    avtDataTreeIterator::PreExecute();

    const char *displace_var = variable.c_str();
    if (strcmp(displace_var, "default") == 0)
        displace_var = pipelineVariable;

    avtDataAttributes &inAtts = GetInput()->GetInfo().GetAttributes();
    if (! inAtts.ValidVariable(displace_var))
    {
        // let it pass through and see what happens
        debug1 << "The displace operator was checking to see if the input "
               << "variable was valid and found that the input data "
               << "attributes did not have a valid entry for it.  Rather "
               << "throw an error, normal processing is proceeding, with "
               << "hopes that the ExecuteData method can succeed." << endl;
    }
}


// ****************************************************************************
//  Method: avtDisplaceFilter::PostExecute
//
//  Purpose:
//      Finds the extents once we have been transformed and set those.
//
//  Programmer: Hank Childs
//  Creation:   May 24, 2004
//
//  Modifications:
//
//    Hank Childs, Fri May 18 16:19:20 PDT 2007
//    Call avtDataTreeIterator's PostExecute, not avtPluginDataTreeIterator, since the 
//    inheritance changed.
//
//    Hank Childs, Thu Aug 26 13:47:30 PDT 2010
//    Change extents names.
//
// ****************************************************************************

void
avtDisplaceFilter::PostExecute(void)
{
    avtDataTreeIterator::PostExecute();

    avtDataAttributes& outAtts = GetOutput()->GetInfo().GetAttributes();

    // get the outputs's spatial extents
    double se[6];
    avtDataset_p output = GetTypedOutput();
    avtDatasetExaminer::GetSpatialExtents(output, se);

    // over-write spatial extents
    outAtts.GetOriginalSpatialExtents()->Clear();
    outAtts.GetThisProcsOriginalSpatialExtents()->Set(se);
}


// ****************************************************************************
//  Method: avtDisplaceFilter::UpdateDataObjectInfo
//
//  Purpose:
//    Indicates that the nodes were transformed.
//
//  Programmer: Kathleen Bonnell
//  Creation:   November 28, 2001
//
//  Modificatons:
//    Kathleen Bonnell, Mon Apr 14 09:54:06 PDT 2003
//    Set CanUseTransform to false.
//
//    Kathleen Bonnell, Wed Jun  2 09:21:46 PDT 2004 
//    Set CanUseInvTransform to false.
//
//    Hank Childs, Fri Jan 13 09:58:47 PST 2006
//    Invalidate spatial meta-data.
//
// ****************************************************************************

void
avtDisplaceFilter::UpdateDataObjectInfo(void)
{
    GetOutput()->GetInfo().GetValidity().SetPointsWereTransformed(true);
    //
    // This operator invalidates any transform matrix in the pipeline.
    //
    GetOutput()->GetInfo().GetAttributes().SetCanUseTransform(false);
    GetOutput()->GetInfo().GetAttributes().SetCanUseInvTransform(false);
    GetOutput()->GetInfo().GetValidity().InvalidateSpatialMetaData();
}


// ****************************************************************************
//  Method: avtDisplaceFilter::ModifyContract
//
//  Purpose:
//    Turn on Zone numbers flag if needed, so that original cell array
//    will be propagated throught the pipeline.
//
//  Programmer: Kathleen Bonnell
//  Creation:   November 28, 2001
//
//  Modifications:
//    Kathleen Bonnell, Wed Jun 19 12:28:10 PDT 2002 
//    Don't turn off Zone numbers if they have been turned on elsewhere in
//    the pipeline.
//
//    Kathleen Bonnell, Wed Jun 19 13:42:37 PDT 2002
//    Completely removed the code turning off zone numbers.  Why set a flag
//    to false if it is already false?  False is the default setting.
//
//    Kathleen Bonnell, Wed Jun  2 09:21:46 PDT 2004 
//    Turn on node numbers when required.
//
// ****************************************************************************

avtContract_p
avtDisplaceFilter::ModifyContract(avtContract_p spec)
{
    avtContract_p rv = new avtContract(spec);
    if (rv->GetDataRequest()->MayRequireZones())
    {
        rv->GetDataRequest()->TurnZoneNumbersOn();
    }
    if (rv->GetDataRequest()->MayRequireNodes())
    {
        rv->GetDataRequest()->TurnNodeNumbersOn();
    }
    return rv;
}

